package com.rules.admin.unit.expect;

import com.gillion.grule.client.core.Context;
import com.gillion.grule.client.logger.LoggerAppenderHolder;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.rules.admin.utils.JSONUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ObjectUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class ResultExpect extends Expect {
   private Object expectResult;

   public ExpectMatcherResult expect(Context context) {
      Object realResult = context.getRtnValue();
      if (realResult != null) {
         String result = JSONUtils.write(realResult);
         realResult = JSONUtils.read(result);
      }

      LoggerAppenderHolder.log("开始匹配期望结果：{}", new Object[]{this.expectResult});
      return this.buildResult(realResult, this.expectResult, context);
   }

   private ExpectMatcherResult buildResult(Object realResult, Object expectResult, Context context) {
      String message = StringUtils.isEmpty(this.errorMessage) ? "期望结果:【{" + JSONUtils.write(expectResult) + "}】与实际结果：【{" + JSONUtils.write(context.getRtnValue()) + "}】不匹配" : this.errorMessage;
      ExpectMatcherResult matcherResult = ExpectMatcherResult.ofResultExpect();
      if (expectResult == null && realResult == null) {
         matcherResult.setMatch(true);
         return matcherResult;
      } else if (expectResult != null && realResult != null) {
         boolean compare = this.compare(expectResult, realResult);
         if (compare) {
            LoggerAppenderHolder.log("结果集匹配成功。", new Object[0]);
            matcherResult.setMatch(true);
         } else {
            LoggerAppenderHolder.log("结果集匹配失败：{}", new Object[]{message});
            matcherResult.setMessage(message);
            matcherResult.setMatch(false);
         }

         return matcherResult;
      } else {
         matcherResult.setMatch(false);
         matcherResult.setMessage(message);
         return matcherResult;
      }
   }

   private boolean compare(final Map<String, Object> expect, final Map<String, Object> source) {
      if (expect == null && source == null) {
         return true;
      } else if (expect != null && source != null) {
         Set<String> keys = expect.keySet();
         return !Iterables.any(keys, new Predicate<String>() {
            public boolean apply(String key) {
               Object expectValue = expect.get(key);
               Object sourceValue = source.get(key);
               return !ResultExpect.this.compare(expectValue, sourceValue);
            }
         });
      } else {
         return false;
      }
   }

   private boolean compare(List<Object> expectList, List<Object> expectSource) {
      if (expectList.size() != expectSource.size()) {
         return false;
      } else {
         for(int i = 0; i < expectList.size(); ++i) {
            Object expectValue = expectList.get(i);
            Object sourceValue = expectSource.get(i);
            boolean compare = this.compare(expectValue, sourceValue);
            if (!compare) {
               return false;
            }
         }

         return true;
      }
   }

   private boolean compare(Object simpleExpect, Object simpleSource) {
      return ObjectUtils.nullSafeEquals(simpleExpect, simpleSource);
   }

   public Object getExpectResult() {
      return this.expectResult;
   }

   public void setExpectResult(Object expectResult) {
      this.expectResult = expectResult;
   }

   public String toString() {
      return "ResultExpect(expectResult=" + this.getExpectResult() + ")";
   }

   public boolean equals(Object o) {
      if (o == this) {
         return true;
      } else if (!(o instanceof ResultExpect)) {
         return false;
      } else {
         ResultExpect other = (ResultExpect)o;
         if (!other.canEqual(this)) {
            return false;
         } else {
            Object this$expectResult = this.getExpectResult();
            Object other$expectResult = other.getExpectResult();
            if (this$expectResult == null) {
               if (other$expectResult != null) {
                  return false;
               }
            } else if (!this$expectResult.equals(other$expectResult)) {
               return false;
            }

            return true;
         }
      }
   }

   protected boolean canEqual(Object other) {
      return other instanceof ResultExpect;
   }

   public int hashCode() {

      int result = 1;
      Object $expectResult = this.getExpectResult();
      result = result * 59 + ($expectResult == null ? 43 : $expectResult.hashCode());
      return result;
   }
}
